package guda.mrpc.core.client;

import guda.mrpc.core.RequestKey;
import guda.mrpc.core.common.ExecutorServiceFactory;
import guda.mrpc.core.registry.ServiceMeta;
import guda.mrpc.core.transport.ClientHandler;
import guda.mrpc.core.transport.callback.RequestCallback;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by well on 17/5/23.
 */
public class ConnectionFactory {

    private Logger log = LoggerFactory.getLogger(ConnectionFactory.class);

    private Lock lock = new ReentrantLock();

    private Condition handlerStatus = lock.newCondition();

    private final static int parallel = Runtime.getRuntime().availableProcessors() + 1;

    private EventLoopGroup eventLoopGroup = new NioEventLoopGroup(parallel);

    private ConcurrentHashMap<RequestKey, Set<ClientHandler>> mappings = new ConcurrentHashMap<>();

    private static final class ConnectionsHolder {
        private static final ConnectionFactory instance = new ConnectionFactory();
    }

    private ConnectionFactory(){
        Timer timer = new Timer();
        timer.schedule(new MoniterFactoryTask(), 10000, 10000);
    }

    public class MoniterFactoryTask extends TimerTask {

        @Override
        public void run() {
            if(mappings.size()==0){
                log.info("[monitor] client connection count is 0");
            }else{
                Iterator<Map.Entry<RequestKey, Set<ClientHandler>>> iterator = mappings.entrySet().iterator();
                while(iterator.hasNext()){
                    Map.Entry<RequestKey, Set<ClientHandler>> next = iterator.next();
                    Set<ClientHandler> value = next.getValue();
                    Iterator<ClientHandler> iterator1 = value.iterator();
                    StringBuilder buf = new StringBuilder();
                    while(iterator1.hasNext()){
                        buf.append(iterator1.next());
                    }
                    log.info("[monitor] service:" + next.getKey() + ",client size:" + value.size()+",detail:"+buf.toString());
                }
            }

        }
    }

    public static ConnectionFactory instance() {
        return ConnectionsHolder.instance;
    }



    public void connect(ServiceMeta serviceMeta) {
        if(log.isInfoEnabled()){
            log.info("connect to host:"+serviceMeta.getHost()+",port:"+serviceMeta.getPort()+",service:"+serviceMeta.getRequestKey());
        }
        ExecutorServiceFactory.getExecuteService().submit(new RequestCallback(serviceMeta, eventLoopGroup));
    }


    public void addClientHandler(RequestKey requestKey, ClientHandler handler) {
        try {
            lock.lock();
            if (mappings.containsKey(requestKey)) {
                Set<ClientHandler> clientHandlers = mappings.get(requestKey);
                if (!clientHandlers.contains(handler)) {
                    clientHandlers.add(handler);
                }
            } else {
                Set<ClientHandler> clientHandlers = new HashSet<>();
                clientHandlers.add(handler);
                mappings.put(requestKey, clientHandlers);
            }
            handlerStatus.signal();
        } finally {
            lock.unlock();
        }
    }

    public List<ClientHandler> getHandlers(RequestKey requestKey) throws InterruptedException {
        Set<ClientHandler> clientHandlers = mappings.get(requestKey);
        if(clientHandlers == null){
            return null;
        }
        return new ArrayList<>(clientHandlers);
    }


    public void remove(ClientHandler handler) {
        if (mappings.values().size() > 0 && null != handler && mappings.values().contains(handler)) {
            mappings.values().removeAll(Arrays.asList(handler));
        }
    }

    public void remove(ServiceMeta serviceMeta) {
        if (mappings.values().size() > 0 && null != serviceMeta ) {
            Set<ClientHandler> clientHandlers = mappings.get(serviceMeta.getRequestKey());
            if(clientHandlers!=null){
                Iterator<ClientHandler> iteratorClientHandler = clientHandlers.iterator();
                while(iteratorClientHandler.hasNext()){
                    ClientHandler next1 = iteratorClientHandler.next();
                    next1.close();
                }
            }
            mappings.remove(serviceMeta.getRequestKey());
        }
    }

    public void shutdown() {
        ExecutorServiceFactory.getExecuteService().shutdown();
        eventLoopGroup.shutdownGracefully();
    }
}
